/*******************************
 * mm.c
 *
 * Source code for the dynamic memory manager
 *
 ******************************/

#include "mm.h"
#include <stdio.h>
#include <stdlib.h>

int memory_leaks = 0;
int buffer_overflows = 0;
int free_errors = 0;
int initialized = 0;
unsigned long pool_size;

int mm_init(unsigned long size) 
{
	if(initialized)
	{
		printf("error: attempting to initialize data structures more than once\n");
		return -1;
	}
	if(size == 0)
	{
		printf("warning: you've just initialized a memory pool that can store exactly 0 bytes. don't expect it to be able to do much.\n");
	}
	initialized = 1;
	pool_size = size;
	pool = malloc(pool_size * sizeof(char));
	memstatus = malloc(size * sizeof(byteinfo));
	if ((pool == NULL) || (memstatus == NULL))
	{
		printf("error: failed to initialize memory pool\n");
		return -1;
	}
	int i;
	for (i=0; i<size; i++)
	{
		memstatus[i].in_use = 0;
		memstatus[i].chunk_size = 0;
	}
	return 0;
}

int mm_end()
{
	if(!initialized)
	{
		printf("error: call to end without corresponding call to initialized\n");
		return -1;
	}
	free(pool);
	initialized = 0;
	printf("%i Memory Leak(s)\n%i Buffer Overflow(s)\n%i Free Error(s)\n", memory_leaks, buffer_overflows, free_errors);
	return 0;
}

char *mm_alloc(unsigned long no_of_chars) 
{
	if(!initialized)
	{
		printf("error: must initialize pool before making call to mm_alloc\n");
		return NULL;
	}
	if(no_of_chars > pool_size)
	{
		printf("error: trying to allocate more memory than was initialized\n");
		return NULL;
	}
	if(no_of_chars == 0)
	{
		printf("warning: you have just allocated 0 bytes of memory. returning a null pointer.\n");
		return NULL;
	}
	unsigned long i, j;
	char *mem_pointer = NULL;
	for(i=0;i<pool_size;i++)
	{
		for(j=0;j<no_of_chars;j++)
		{
			if(memstatus[i + j].in_use)
				break;
			if(j == (no_of_chars - 1)) 
				mem_pointer = &pool[i];
		}
		if (mem_pointer != NULL)
			break;
	}
	if (mem_pointer == NULL)
		printf("error: insufficient memory for call to mm_alloc\n");
	else
	{
		int array_pos = i;
		memstatus[array_pos].chunk_size = no_of_chars;
		for(j=0; j<no_of_chars; j++)
		{
			memstatus[array_pos + j].in_use = 1;
		}
		memory_leaks++;
	}
	return mem_pointer;
}

int mm_free(char *ptr) 
{
	if(!initialized)
	{
		printf("error: must initialize pool before making call to mm_free\n");
		return -1;
	}
	if((int)ptr < (int)pool || (int)ptr > (int)&pool[pool_size - 1])
	{
		printf("error: trying to free something that isn't in the memory pool\n");
		free_errors++;
		return -1;
	}
	int array_pos = ptr - pool;
	if(memstatus[array_pos].chunk_size <= 0)
	{
		printf("error: trying to free something that hasn't been properly allocated\n");
		free_errors++;
		return -1;
	}
	int i;
	for(i=0; i<memstatus[array_pos].chunk_size; i++)
	{
		memstatus[array_pos + i].in_use = 0;
	}
	memstatus[array_pos].chunk_size = 0;
	memory_leaks--;
	return 0;
}

int mm_assign(char *ptr, char val) 
{
	if(!initialized)
	{
		printf("error: must initialize pool before making call to mm_assign\n");
		return -1;
	}
	if((int)ptr < (int)pool || (int)ptr > (int)&pool[pool_size - 1])
	{
		printf("error: trying to write to a location that isn't in the memory pool\n");
		return -1;
	}
	int array_pos = ptr - pool;
	if(memstatus[array_pos].in_use == 0)
	{
		printf("error: trying to write to a location that is not allocated\n");
		buffer_overflows++;
		return -1;
	}
	pool[array_pos] = val;
	return 0;
}

